package com.rtx.edi.gatway.handle;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

import com.alibaba.fastjson.JSON;
import com.rtx.common.EdiConstant;
import com.rtx.edi.gatway.bean.response.GatWayResponse;
import com.rtx.edi.gatway.cache.EdiApiContext;
import com.rtx.edi.gatway.cache.EdiApiDataContext;
import com.rtx.edi.log.bean.EdiContentLog;
import com.rtx.edi.log.bean.EdiLog;
import com.rtx.edi.log.service.EdiLogService;

import cn.hutool.core.lang.UUID;

/**
 * 
 * 
 * @author Administrator
 *
 */
public abstract class GatWayHandle {
	
	Logger logger = LoggerFactory.getLogger(this.getClass());
	
	public static ConcurrentHashMap<String, EdiApiContext> cache = new ConcurrentHashMap<String, EdiApiContext>();
	
	ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 100, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100));
	
	@Autowired
	EdiLogService ediLogService;
	
	@Autowired
	protected ApplicationContext context;
	
	/**
	 * @param request  请求报文
	 * @param configCode 配置编码
	 */
	public String handleRequest(String request,String configCode) {
		GatWayResponse response = new GatWayResponse();
		EdiLog ediLog = new EdiLog();
		ediLog.setStarttime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
		
		try {
			//从缓存中查配置信息
			EdiApiContext apiContext = init(configCode);
			
			//创建数据上下文对象
			EdiApiDataContext dataApiContext = new EdiApiDataContext(apiContext,request);
			
			initEdiLog(ediLog,dataApiContext);
			
			//将源报文转换成目标报文 & 校验源报文中节点不能为空
			convertTargetRequest(dataApiContext);
			
			//确实数据仓库号
			determineWhseid(dataApiContext);
			
			//执行自定义验证逻辑
			executerLoadValidate(dataApiContext);
			
			//生成 wms流水号 uuid 等等
			buildApiGlobalMap(dataApiContext);
			
			//填充报文数据到表字段 生成报文与表字段
			buildTableValueObject(dataApiContext);
			
			//执行自定义逻辑
			executerPreSaveCustom(dataApiContext);
			
			//决定是数据更新还是保存
			determineWhseidSaveOrUpdate(dataApiContext);
			
			if(EdiConstant.ACTIVE_SAVE.equals(dataApiContext.getDetermineSaveOrUpdate())) {
				//执行新增逻辑
				executerSave(dataApiContext);
			}else if(EdiConstant.ACTIVE_UPDATE.equals(dataApiContext.getDetermineSaveOrUpdate())) {
				//执行更新逻辑
				executerUpdate(dataApiContext);
			}else {
				//报错
				throw new RuntimeException("判断当前下发数据更新或者新增失败!");
			}
			
			//执行插入或者更新后自定义逻辑
			executerAfterSaveCustom(dataApiContext);
			
			response.setCode("200");
			response.setMessage("成功");
			ediLog.setStatus("S");
		} catch (Exception e) {
			logger.error("",e);
			response.setCode("500");
			response.setMessage(e.getMessage());
			ediLog.setStatus("F");
		}
		
		ediLog.getEdiContentLog().setResponse(JSON.toJSONString(response));
		
		//记录日志
		insertLogs(ediLog);
		
		return JSON.toJSONString(response);
		//生成返回报文
	} 
	
	protected void insertLogs(EdiLog log) {
		try {
			pool.execute(()->{
				ediLogService.addlogandcontent("ris_edi", log);
	        });
			
		} catch (Exception e) {
			logger.error("",e);
		}
	};
	
	protected void initEdiLog(EdiLog ediLog,EdiApiDataContext dataApiContext) {
		try {
			String uid = UUID.randomUUID().toString();
			ediLog.setInsTime(new Date());
			ediLog.setLogno(uid);
			ediLog.setEdiapiname(dataApiContext.getApiContext().getEdiApi().getName());
			EdiContentLog contentLog = new EdiContentLog();
			contentLog.setApicontext(JSON.toJSONString(dataApiContext.getApiContext()));
			contentLog.setLogno(uid);
			contentLog.setRequest(dataApiContext.getSourceRequestStr());
			contentLog.setInsTime(new Date());
			ediLog.setEdiContentLog(contentLog);
			
			dataApiContext.setEdiLog(ediLog);
		} catch (Exception e) {
			logger.info("初始化记录日志异常",e);
		}
	}
	
	/**
	 * 将源报文转换成JSON对象
	 * @param apiContext
	 */
	abstract protected void convertTargetRequest(EdiApiDataContext apiContext);
	
//	/**
//	 * 执行校验报文中某个节点是否为空
//	 * @param apiContext
//	 */
//	abstract protected void commonValidate(EdiApiDataContext apiContext);
//	
	/**
	 * 执行自定义验证逻辑
	 * @param apiContext
	 */
	abstract protected void executerLoadValidate(EdiApiDataContext apiContext);
	
	/**
	 * 填充报文数据到表字段 生成报文与表字段Map对象
	 * @param apiContext
	 */
	abstract protected void buildTableValueObject(EdiApiDataContext apiContext);
	
//	/**
//	 * 执行配置项 将值再次填充到表字段中
//	 */
//	abstract protected void fillTableValueByConfig(EdiApiDataContext apiContext);
	
	
	/**
	 * 执行在插入前执行 自定义逻辑
	 * @param apiContext
	 */
	abstract protected void executerPreSaveCustom(EdiApiDataContext apiContext);
	
	/**
	 * 执行保存逻辑
	 * @param apiContext
	 */
	abstract protected void executerSave(EdiApiDataContext apiContext);
	
	/**
	 * 执行更新逻辑
	 * @param apiContext
	 */
	abstract protected void executerUpdate(EdiApiDataContext apiContext);
	
	/**
	 * 执行保存或者更新逻辑
	 * @param apiContext
	 */
	abstract protected void executerAfterSaveCustom(EdiApiDataContext apiContext);
	
	/**
	 * 生成 wms流水号 uuid 等等
	 * @param apiContext
	 */
	abstract protected void buildApiGlobalMap(EdiApiDataContext apiContext);
	
	/**
	 * 确定数据仓库号
	 * @param apiContext
	 */
	abstract protected void determineWhseid(EdiApiDataContext apiContext);
	
	/**
	 * 决定是数据是更新还是保存
	 * @param apiContext
	 */
	abstract protected void determineWhseidSaveOrUpdate(EdiApiDataContext apiContext);
	
	/**
	 * 初始化API上下文
	 * @param request
	 * @param configCode
	 * @return
	 */
	protected EdiApiContext init(String configCode) {
		//从缓存中找实现类 cache 中找实现类
		if(cache.containsKey(configCode)) {
			return cache.get(configCode);
		}
		
		//configCode API上下文
		EdiApiContext apiContext = new EdiApiContext();
		
		//从数据库查找配置信息并设置到API上下文
		loadConfigFromDatabase(configCode,apiContext);
//		InitHelp.loadTestSkuConfig(apiContext);
//		InitHelp.loadTestOrdersConfig(apiContext);
		
		//加载 自定义逻辑
		loadCustom(apiContext);
		
		cache.put(configCode, apiContext);
		
		return apiContext;
	};
	
	//从数据库查找配置信息并设置到API上下文
	abstract protected void loadConfigFromDatabase(String configCode,EdiApiContext apiContext);
	
	//加载插入前或者更数据前 自定义逻辑
	abstract protected void loadCustom(EdiApiContext apiContext);
	
}
